package com.jg.visitor.aop;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import com.jg.visitor.GlobalConst;
import com.jg.visitor.annotation.Encrypt;
import com.jg.visitor.annotation.LoginCheck;
import com.jg.visitor.common.bean.ResponseBean;
import com.jg.visitor.common.bean.ServiceCode;
import com.jg.visitor.common.utils.JsonService;
import com.jg.visitor.config.exception.BaseException;
import com.jg.visitor.config.exception.JgException;
import com.jg.visitor.service.GlobalConfigService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@Aspect
@Component
@Slf4j
public class AdminAnnotationAop {
    @Resource
    private GlobalConfigService globalConfigService;
    @Resource
    private JsonService jsonService;


    @Pointcut("@annotation(com.jg.visitor.annotation.LoginCheck)")
    public void loginCheckPoint(){}

    @Pointcut("@annotation(com.jg.visitor.annotation.Encrypt)")
    public void encryptPoint(){}

    @Around("loginCheckPoint()")
    public Object aroundLoginCheck(ProceedingJoinPoint pj) throws Throwable {
        //获取方法
        LoginCheck annotation = ((MethodSignature) pj.getSignature()).getMethod().getAnnotation(LoginCheck.class);

        //获取注解参数
        int userType = annotation.userType();

        //检验对应用户类型
        globalConfigService.checkIsUserType(userType);

        //执行原始方法
        return pj.proceed();
    }

    @Around(value = "encryptPoint() && @annotation(encrypt)")
    public Object aroundEncrypt(ProceedingJoinPoint pj,Encrypt encrypt){
        //不管怎样，保证业务必须执行
        Object interfaceRetValue;
        try{
            interfaceRetValue = pj.proceed();
        }catch (BaseException e){
            log.error("BaseException",e);
            interfaceRetValue = ResponseBean.error(e.getCode(), e.getMessage());
        }catch (JgException e){
            log.error("JgException",e);
            interfaceRetValue = ResponseBean.error(e.getErrorCode());
        }catch (Throwable e){
            log.error("Throwable",e);
            interfaceRetValue = ResponseBean.error(ServiceCode.INTERNAL_SERVER_ERROR);
        }

        return getEncryptData(interfaceRetValue);
    }

    public ResponseBean getEncryptData(Object data){
        //响应的数据转json
        String jsonDataRet = jsonService.objectToJson(data);

        //生成随机的aes16位密钥
        String aesKey = generateAesKey();

        //通过aes_key加密数据
        AES aes = new AES("CBC", "PKCS7Padding",
                // 密钥，可以自定义
                aesKey.getBytes(),
                // iv加盐，按照实际需求添加
                GlobalConst.IV.getBytes());

        //加密后得到AES加密后的json 16进制字符串
        String encryptHexData = aes.encryptHex(jsonDataRet);

        //通过rsa加密
        RSA rsa = new RSA(GlobalConst.RSA_PRIVATE_KEY, GlobalConst.RSA_PUBLIC_KEY);

        //aes_key,先把key转16进制字符串
        String encodeHexStr = HexUtil.encodeHexStr(aesKey, CharsetUtil.CHARSET_UTF_8);

        //再把16进制字符串rsa加密得到加密后的字节数组，
        byte[] encode = rsa.encrypt(encodeHexStr.getBytes(), KeyType.PublicKey);

        //加密后的字节数组，转为base64
        String aesKeyRsaBase64Str = Base64.encode(encode);

        Map<String,String> ret = new HashMap<>();
        ret.put("dataStr",encryptHexData);
        ret.put("aesKeyRsaStr",aesKeyRsaBase64Str);

        return ResponseBean.success(ret);
    }

    private String generateAesKey(){
        String str = "0123456789";

        String[] split = str.split("");

        StringBuilder finalStr = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            int nextInt = new Random().nextInt(10);
            finalStr.append(split[nextInt]);
        }

        return finalStr.toString();
    }
}
